import { CookieOptions, NextFunction, Request, Response } from "express";
import { RegisterSchema, LoginSchema } from "../schemas";
import { UserService } from "../services";
import { AppError } from "../utils";
import { User } from "../entities";
import { config } from "dotenv";
import { SignOptions, sign } from "jsonwebtoken";

config();

const expiresIn = Number(process.env.EXPIRES_IN) || 1440;
const jwtSecret = process.env.JWT_SECRET || "";

export class AuthController {
  private static readonly cookieOptions: CookieOptions = {
    httpOnly: true,
    expires: new Date(Date.now() + expiresIn * 60 * 1000),
    maxAge: expiresIn * 60 * 1000,
    ...(process.env.NODE_ENV === "production" && {
      secure: true,
    }),
  };

  private static readonly jwtOptions: SignOptions = {
    expiresIn: `${expiresIn}m`,
  };

  public static async register(
    req: Request<unknown, unknown, RegisterSchema>,
    res: Response,
    next: NextFunction,
  ) {
    try {
      const { name, email, password } = req.body;
      if (!name || !email || !password) {
        return res.status(400).json({
          status: "fail",
          message: "Invalid parameters",
        });
      }
      const newUser = await UserService.create({
        name,
        email: email.toLowerCase(),
        password,
      });
      await newUser.save();
      res.sendStatus(200);
    } catch (err) {
      if (err.code === "23505") {
        return res.status(409).json({
          status: "fail",
          message: "User with such email already exist",
        });
      }
      next(err);
    }
  }

  public static async login(
    req: Request<unknown, unknown, LoginSchema>,
    res: Response,
    next: NextFunction,
  ) {
    try {
      const { name, password } = req.body;
      const user = await UserService.findByName(name);
      const isMatched = await User.comparePasswords(
        password,
        user?.password ?? "",
      );
      if (!user || !isMatched) {
        return next(
          new AppError(400, !user ? "Invalid login" : "Invalid password"),
        );
      }
      const token = sign({ data: user }, jwtSecret, AuthController.jwtOptions);
      res.cookie("token", token, AuthController.cookieOptions);
      res.sendStatus(200);
    } catch (err) {
      next(err);
    }
  }

  public static async logout(req: Request, res: Response, next: NextFunction) {
    try {
      res.cookie("token", "", { maxAge: 1 });
      res.sendStatus(200);
    } catch (err) {
      next(err);
    }
  }
}
